package web.dropwizard;

import application.services.ApplicationServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jersey.api.client.Client;
import domain.model.project.support.serializer.ProjectModule;
import infra.jenkins.JenkinsService;
import infra.jenkins.config.JenkinsServiceConfig;
import infra.mongodb.MongoDBProjectRepository;
import io.dropwizard.Application;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.client.JerseyClientConfiguration;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import util.jackson.serializer.DiscoverableSubtypeValueObjectModule;
import util.mongodb.MongoDBDataSource;
import web.dropwizard.configuration.JenkinsConfig;
import web.dropwizard.configuration.ProjectConfiguration;
import web.dropwizard.healthCheck.JenkinsHealthCheck;
import web.dropwizard.healthCheck.MongoHealthCheck;
import web.dropwizard.managed.MongoDBLifecycleManaged;
import web.dropwizard.mapper.InvalidJSONDocumentExceptionMapper;
import web.dropwizard.mapper.NoProjectExceptionMapper;

/**
 * @author Gregory Boissinot
 */
public class ProjectApplication extends Application<ProjectConfiguration> {

    public static void main(String[] args) throws Exception {
        new ProjectApplication().run(args);
    }

    @Override
    public void initialize(Bootstrap<ProjectConfiguration> bootstrap) {
    }

    @Override
    public void run(ProjectConfiguration projectConfiguration, Environment environment) throws Exception {
        final Client httpClient = getHttpClient(environment, projectConfiguration.getJerseyClientConfiguration());

        setLifecycleManagedObject(projectConfiguration, environment);
        setHealthCheck(projectConfiguration, environment, httpClient);
        setCustomObjectMapper(environment);
        setJerseyMappers(environment);
        setRESTResources(projectConfiguration, environment, httpClient);
    }

    private void setJerseyMappers(Environment environment) {
        environment.jersey().register(InvalidJSONDocumentExceptionMapper.class);
        environment.jersey().register(NoProjectExceptionMapper.class);
    }

    private void setLifecycleManagedObject(ProjectConfiguration projectConfiguration, Environment environment) {
        environment.lifecycle().manage(new MongoDBLifecycleManaged(projectConfiguration.getMongoConfig().getMongoDBDataSource().getMongo()));
    }

    private void setHealthCheck(ProjectConfiguration projectConfiguration, Environment environment, Client httpClient) {

        MongoDBDataSource mongoDBDataSource = projectConfiguration.getMongoConfig().getMongoDBDataSource();
        environment.healthChecks().register("mongo", new MongoHealthCheck(mongoDBDataSource));

        JenkinsConfig jenkinsConfig = projectConfiguration.getJenkinsConfig();
        environment.healthChecks().register("jenkins", new JenkinsHealthCheck(httpClient, jenkinsConfig.getUrl(), jenkinsConfig.getRootTemplateDir()));
    }

    private void setRESTResources(ProjectConfiguration projectConfiguration, Environment environment, Client httpClient) {
        MongoDBDataSource mongoDBDataSource = projectConfiguration.getMongoConfig().getMongoDBDataSource();
        final MongoDBProjectRepository projectRepository = new MongoDBProjectRepository(mongoDBDataSource);
        final JenkinsService ciService = getCiService(projectConfiguration, environment, httpClient);
        ProjectResource projectResource = new ProjectResource(new ApplicationServiceImpl(projectRepository, ciService));
        environment.jersey().register(projectResource);
    }

    private JenkinsService getCiService(ProjectConfiguration projectConfig, Environment environment, Client httpClient) {
        JenkinsConfig jenkinsConfig = projectConfig.getJenkinsConfig();
        JenkinsServiceConfig jenkinsServiceConfig =
                new JenkinsServiceConfig(httpClient, jenkinsConfig.getUrl(), jenkinsConfig.getRootTemplateDir());
        if (jenkinsConfig.getUsername() != null) {
            jenkinsServiceConfig.setUsername(jenkinsConfig.getUsername());
        }
        if (jenkinsConfig.getPassword() != null) {
            jenkinsServiceConfig.setPassword(jenkinsConfig.getPassword());
        }
        return new JenkinsService(jenkinsServiceConfig);
    }

    private void setCustomObjectMapper(Environment environment) {
        ObjectMapper objectMapper = environment.getObjectMapper();
        objectMapper.registerModule(new DiscoverableSubtypeValueObjectModule());
        objectMapper.registerModule(new ProjectModule());
    }

    private Client getHttpClient(Environment environment, JerseyClientConfiguration jerseyClientConfiguration) {
        return new JerseyClientBuilder(environment).using(jerseyClientConfiguration).build(getName());
    }

}
